<?php

/*
 Copyright (c) 2009 hamcrest.org
 */

/**
 * Controls the process of extracting @factory doctags
 * and generating factory method files.
 *
 * Uses File_Iterator to scan for PHP files.
 */
class FactoryGenerator
{
  /**
   * Path to the Hamcrest PHP files to process.
   *
   * @var string
   */
  private $path;

  /**
   * @var array of FactoryFile
   */
  private $factoryFiles;

  public function __construct($path) {
    $this->path = $path;
    $this->factoryFiles = array();
  }

  public function addFactoryFile(FactoryFile $factoryFile) {
    $this->factoryFiles[] = $factoryFile;
  }

  public function generate() {
    $classes = $this->getClassesWithFactoryMethods();
    foreach ($classes as $class) {
      foreach ($class->getMethods() as $method) {
        foreach ($method->getCalls() as $call) {
          foreach ($this->factoryFiles as $file) {
            $file->addCall($call);
          }
        }
      }
    }
  }

  public function write() {
    foreach ($this->factoryFiles as $file) {
      $file->build();
      $file->write();
    }
  }

  public function getClassesWithFactoryMethods() {
    $classes = array();
    $files = $this->getSortedFiles();
    foreach ($files as $file) {
      $class = $this->getFactoryClass($file);
      if ($class !== null) {
        $classes[] = $class;
      }
    }
    return $classes;
  }

  public function getSortedFiles() {
    $iter = File_Iterator_Factory::getFileIterator($this->path, '.php');
    $files = array();
    foreach ($iter as $file) {
      $files[] = $file;
    }
    sort($files, SORT_STRING);
    return $files;
  }

  public function getFactoryClass($file) {
    $name = $this->getFactoryClassName($file);
    if ($name !== null) {
      require_once $file;
      if (class_exists($name)) {
        $class = new FactoryClass(substr($file, strpos($file, 'Hamcrest/')), new ReflectionClass($name));
        if ($class->isFactory()) {
          return $class;
        }
      }
    }
    return null;
  }

  public function getFactoryClassName($file) {
    $content = file_get_contents($file);
    if (preg_match('/\n\s*class\s+(\w+)\s+extends\b/', $content, $match)
        && preg_match('/@factory\b/', $content)) {
      return $match[1];
    }
    return null;
  }
}
